package za.org.jcicada.syshealth;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

import za.org.jcicada.cfgmngr.IConfigurationServer;
import za.org.jcicada.cfgmngr.IPhysicalServer;
import za.org.jcicada.cfgmngr.impl.DefaultPhysicalClient;
import za.org.jcicada.genss.PhysicalURN;
import za.org.jcicada.genss.sensor.SensorURN;
import za.org.jcicada.genss.sensor.impl.Sensor;

public class PhysicalModel extends DefaultTreeModel {

	private static final long serialVersionUID = 1L;
	
	private Map<PhysicalURN, DefaultMutableTreeNode> nodeMap = new HashMap<PhysicalURN, DefaultMutableTreeNode>();
	
	private Map<PhysicalURN, SensorURN> sensorURNMap = new HashMap<PhysicalURN, SensorURN>();
	
	private Map<PhysicalURN, Sensor> sensorMap = new HashMap<PhysicalURN, Sensor>();
	
	private Map<Sensor, PhysicalURN> sensorPhysicalMap = new HashMap<Sensor, PhysicalURN>();
	
	private SensorCache sensorCache;
	
	private Model model;
	
	private PhysicalClient physicalClient = new PhysicalClient();
	
	private List<PhysicalURN> unknownPhysicalURNs = new ArrayList<PhysicalURN>();
	
	private String view = "default";
	
	private Observer sensorObserver = new Observer() {

		public void update(Observable sensor, Object arg1) {
			PhysicalURN physicalURN = sensorPhysicalMap.get(sensor);
			if (physicalURN != null) {
				fireNodeChanged(physicalURN);
			}
		}
		
	};

	public PhysicalModel(Model model) {
		super(new DefaultMutableTreeNode(PhysicalURN.getRootURN()));
		this.model = model;
		nodeMap.put(PhysicalURN.getRootURN(), (DefaultMutableTreeNode) getRoot());
		List<PhysicalURN> physicalURNs = new ArrayList<PhysicalURN>();
		physicalURNs.add(PhysicalURN.getRootURN());
		requestViewSensors(view, physicalURNs);
	}
	
	public void initialize(IConfigurationServer configServer) {
		sensorCache = new SensorCache(configServer);
	}
	
	public void setChildrenOf(PhysicalURN parentURN, List<PhysicalURN> childrenURNs) {
		DefaultMutableTreeNode treeNode = nodeMap.get(parentURN);
		for (PhysicalURN childURN :childrenURNs) {
			DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(childURN);
			treeNode.add(childNode);
			nodeMap.put(childURN, childNode);
		}
		requestViewSensors(view, childrenURNs);
		fireTreeNodesInserted(treeNode, treeNode.getPath(), null, null);
	}
	
	public SensorURN getSensorURN(PhysicalURN physicalURN) {
		return sensorURNMap.get(physicalURN);
	}
	
	public Sensor getSensor(PhysicalURN physicalURN) {
		return sensorMap.get(physicalURN);
	}
	
	private void setSensorURNOf(final PhysicalURN physicalURN, final SensorURN sensorURN) {
		sensorURNMap.put(physicalURN, sensorURN);
		Sensor sensor = sensorMap.get(physicalURN);
		if (sensor != null) {
			sensorMap.remove(physicalURN);
			sensorPhysicalMap.remove(sensor);
			sensor.deleteObserver(sensorObserver);
		}
		sensorCache.attach(new SensorCache.Callback() {

			public void attached(SensorURN sensorURN, Sensor sensor) {
				sensorMap.put(physicalURN, sensor);
				sensorPhysicalMap.put(sensor, physicalURN);
				sensor.addObserver(sensorObserver);
				fireNodeChanged(physicalURN);
			}
			
		}, sensorURN);
	}
	
	private void fireNodeChanged(final PhysicalURN physicalURN) {
		DefaultMutableTreeNode treeNode = nodeMap.get(physicalURN);
		fireTreeNodesChanged(treeNode, treeNode.getPath(), null, null);
	}
	
	private void requestViewSensors(String view, Collection<PhysicalURN> physicalURNs) {
		unknownPhysicalURNs.addAll(physicalURNs);
		IPhysicalServer physicalServer = model.getPhysicalServer();
		if (physicalServer != null) {
			physicalServer.requestViewSensors(physicalClient, view, unknownPhysicalURNs);
			unknownPhysicalURNs.clear();
		}
	}
	
	public void setView(String view) {
		if (!this.view.equals(view)) {
			this.view = view;
			requestViewSensors(view, nodeMap.keySet());
		}
	}

	public String getView() {
		return view;
	}

	private class PhysicalClient extends DefaultPhysicalClient {

		@Override
		public void informViewSensor(String view, PhysicalURN physicalURN, SensorURN sensorURN) {
			setSensorURNOf(physicalURN, sensorURN);
		}
		
	}

}
